建立OpenAccess ORM的監聽器
前一篇文章,我們透過設定物件容器的 Log 屬性來取得真正執行的 Sql 語法,但是 Log 屬性限制必須使用 TextWriter 物件,本篇文章就用另一種方式,可以讓我們自由設計要記錄 Sql 語法的方式。
OpenAccessContext.Log 屬性必須使用 System.IO.TextWriter 型別之物件,來收集真正執行的 Sql 語法,但是有些環境下,我們會希望自訂收集 Sql 語法的方式,本篇文章範例,我們就用一個 StringBuilder 物件做為收集體,然後呼叫端就可以透過 StringBuilder.ToString() 取得記錄下來的資料。
首先,我們延續上一篇的 ProductMgmt 類別,做點小修改,以下僅列出調整的部分:
Public Class ProductsMgmt
Private cxt As SecondModel
Private output As IO.StringWriter
Private tracer As MyNorthwindTraceListener
Public Sub New()
cxt = New SecondModel()
output = New IO.StringWriter()
cxt.Log = output
tracer = New MyNorthwindTraceListener(New Text.StringBuilder())
End Sub
Public Function GetLogString() As String
If tracer IsNot Nothing Then
Return tracer.GetLogString()
Else
Return Nothing
End If
End Function
......
......
......
End Class
主要是在建構式,我們多加一個建立 MyNorthwindTraceListener 物件的程式,另外加入一個 GetLogString() 函式,回傳 tracer.ToString() 結果。
接下來我們建立 MyNorthwindTraceListener 類別:
Imports System.Threading
Imports Telerik.OpenAccess.Diagnostics
Imports System.Text
Public Class MyNorthwindTraceListener
Inherits TraceListener
Private id As Integer
Private count As Integer
Private _sb As StringBuilder
Private myThread As Thread
Public Sub New(ByVal sb As StringBuilder)
MyBase.New("Northwind Trace Listener")
Me.myThread = Thread.CurrentThread
Me._sb = sb
Me.count = 0
TraceAdapter.Instance.Level = "4"
Me.id = TraceAdapter.Instance.Listeners.Add(Me)
End Sub
Public Overloads Overrides Sub Write(ByVal message As String)
Me.WriteLine(message)
End Sub
Public Overloads Overrides Sub WriteLine(ByVal message As String)
If Object.ReferenceEquals(Me.myThread, Thread.CurrentThread) = False Then
Return
End If
If message.StartsWith("driver.stat.exec") Then
count += 1
Dim index As Integer
If message.IndexOf("SELECT") >= 0 Then
index = message.IndexOf("SELECT")
ElseIf message.IndexOf("INSERT") >= 0 Then
index = message.IndexOf("INSERT")
ElseIf message.IndexOf("UPDATE") >= 0 Then
index = message.IndexOf("UPDATE")
ElseIf message.IndexOf("DELETE") >= 0 Then
index = message.IndexOf("DELETE")
End If
_sb.AppendLine(String.Format("{0}: {1}" & vbCrLf, count, message.Substring(index)))
End If
End Sub
Friend Sub Reset()
Me._sb = New StringBuilder()
Me.count = 0
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
_sb.AppendLine(Me.count.ToString() & " quires executed")
MyBase.Dispose(disposing)
End Sub
Public Function GetLogString() As String
Return _sb.ToString()
End Function
End Class
上述程式碼最重要的部分,就是要繼承 TraceListener 類別,並在建構子中,把自己加入 Listeners 集合中,若少了這個動作,就不會記錄 Log 了。建構式的傳入參數,就是用來收集資料的物件,在此我們使用 StringBuiler。
WirteLine() 則是把 OpenAccess ORM 記錄下來的訊息物件(message)做適當的處理(因為 message 中會包含許多 ORM 架構之訊息資料,我們在此只擷取 Sql 語法),然後調用 StringBuiler.AppendLine() 把處理的結果儲存下來。
最後我們建立一個 GetLogString() 函式,回傳 StringBuiler.ToString() 的結果。
現在我們延用上一篇的測試案例(純脆是求快而已,正常應該要再加一個測試案例):
<TestMethod()> _
Public Sub TraceSQLStatementsTest()
Dim target As ProductsMgmt = New ProductsMgmt()
Dim actual As IList(Of Products)
actual = target.GetGiantProducts
Assert.IsTrue(actual.Any)
Assert.IsNotNull(target.TraceSQLStatements())
Assert.IsNotNull(target.GetLogString())
End Sub
測試通過後,我們一樣延用前一篇的 ShowTraceSQLStatements.aspx 頁面,但是在程式中把原本輸出的地方調整一下:
Private Sub DataBinding(ByVal bo As ProductsMgmt)
gvList.DataSource = bo.GetGiantProducts()
gvList.DataBind()
'lblSQL.Text = bo.TraceSQLStatements()
lblSQL.Text = bo.GetLogString().Replace(vbCrLf, "<br />")
End Sub
執行結果如下:
上述 Gif 圖檔的直接連結:http://i.minus.com/iopqGlRKD2QOx.Gif